package arXiv;
/*
 * AtomParser.java
 * Parses the output of the Atom feed from arXiv
 */
 
 import java.net.*;
 import java.io.*;
 
 import java.lang.Math;
 
public class AtomParser {
    
	private URL queryURL;
	
	// object for storing results
	public ParseOutput myParseOutput;
		
	
    public AtomParser() {
		queryURL = null;
	}
	
	public AtomParser(URL inputURL) {
		queryURL = inputURL;
	}
	
	public URL getURL() {
		return queryURL;
	}
	
	public ParseOutput doParsing() {
	
		myParseOutput = new ParseOutput();
		
		try {
			BufferedReader in = new BufferedReader(
					new InputStreamReader(queryURL.openStream()));

			String inputLine;
			
			// TODO: check the case of 0 results, and the case of a single error entry
			
			// first read in metadata
			
			// read in xml version
			inputLine = in.readLine();
			check(inputLine, "?xml");
			// read in feed xml namespace
			inputLine = in.readLine();
			check(inputLine, "feed");
			// read in link
			inputLine = in.readLine();
			check(inputLine, "link");
			// read in title
			inputLine = in.readLine();
			check(inputLine, "title");
			// read in id
		    inputLine = in.readLine();
			check(inputLine, "id");
			// read in updated
			inputLine = in.readLine();
			check(inputLine, "updated");
			
			//read in results
			inputLine = in.readLine();
			check(inputLine, "opensearch:totalResults");
			// get total number of result entries from this
			myParseOutput.numEntries = Integer.parseInt(readValue(inputLine));
			// read in start index
			inputLine = in.readLine();
			check(inputLine, "opensearch:startIndex");
			myParseOutput.startIndex = Integer.parseInt(readValue(inputLine));
			// read in results per page
			inputLine = in.readLine();
			check(inputLine, "opensearch:itemsPerPage");
			myParseOutput.itemsPerPage = Integer.parseInt(readValue(inputLine));
			myParseOutput.items = Math.min(myParseOutput.itemsPerPage, myParseOutput.numEntries);
			System.out.println("Displaying " + myParseOutput.items + " results"); 
			
			int numEntriesSoFar = 0;
			
			myParseOutput.initializeArray();
			
			while ((inputLine = in.readLine()) != null && !inputLine.equals("</feed>")) {
				if (numEntriesSoFar >= myParseOutput.itemsPerPage){
					System.out.println("Ignoring some entries");
					break;
				}				
				// now read in the individual entries
				// title, url, abstract, etc. 
				

				// add an entry to the list
				Article currEntry = new Article();
				
				// read in entry id
				inputLine = in.readLine();	
				check(inputLine, "id");
				currEntry.arXivId = readValue(inputLine);
				
				// read in date updated
				inputLine = in.readLine();
				check(inputLine, "updated");
				currEntry.updated = readValue(inputLine);
				
				// read in date published
				inputLine = in.readLine();
				check(inputLine, "published");
				currEntry.pubDate = readValue(inputLine);
				
				// read in title
				inputLine = in.readLine();
				check(inputLine, "title");
				currEntry.title = readValue(inputLine);
				
				// read in summary
				inputLine = in.readLine();
				check(inputLine, "summary");
				//System.out.println(inputLine);
				currEntry.summary = readValue(inputLine);
				while ((inputLine = in.readLine())!=null && !inputLine.equals("</summary>")) {
					currEntry.summary += " ";
					currEntry.summary += inputLine;
					//System.out.println(inputLine);
				}
				//System.out.println(currEntry.summary);
				
				// read in authors
				//inputLine = in.readLine();
				while (readElementName(inputLine = in.readLine()).equals("author")) {
					// name of author
					String currAuthor = readValue(in.readLine());
					currEntry.authors.add(currAuthor);
					//System.out.println(currAuthor);
					inputLine = in.readLine();   // </author>
					check(inputLine, "/author");
					// TODO: author affiliation
				}
				
				// read in doi (optional)
				if (readElementName(inputLine).equals("arxiv:doi")) {
					currEntry.doi = readValue(inputLine);
					inputLine = in.readLine();
				}
				
				// read in comments
				if (readElementName(inputLine).equals("arxiv:comment")) {
					currEntry.comment = "";
					while (!inputLine.contains("</arxiv:comment>")) {
						currEntry.comment += readValue(inputLine);
						//System.out.println(inputLine);
						inputLine = in.readLine();
					}
					// get last line
					currEntry.comment += readValue(inputLine);
				}
				
				//System.out.println(currEntry.comment);
							
				// read in journal ref
				inputLine = in.readLine();
				if (readElementName(inputLine).equals("arxiv:journal_ref")) {
					currEntry.journalref = readValue(inputLine);
					inputLine = in.readLine();
				}
				
				// read in links
				while (readElementName(inputLine).equals("link")) {
					// up to 3 link elements
					//System.out.println("Reading in a link");
					inputLine = in.readLine();
				}
				
				// read in primary category
				check(inputLine,"arxiv:primary_category");
				//currEntry.primaryCat = readValue(inputLine); category is different
				
				// read in categories
				inputLine = in.readLine();
				while (readElementName(inputLine).equals("category")) {
					//System.out.println("reading in a category");
					inputLine = in.readLine();
				}
				
				// should be end entry
				if (!check(inputLine, "/entry")) {
					System.err.println("Error in entry");
					break;
				}
				
				// add to list of entries
				myParseOutput.entries[numEntriesSoFar] = currEntry;
				numEntriesSoFar++;
				
				System.out.println("Entry " + numEntriesSoFar);
				
				System.out.println("Title = " + currEntry.title);
				
				System.out.println("Authors = " + currEntry.authors);
							
				System.out.println("Summary = " + currEntry.summary);
				
			}	
			
		} catch (IOException e) {
					System.err.println("Something wrong with Atom feed ");
		}
		
  		
		
		return myParseOutput;
	}
	
	
	private String readValue (String line) {
	// read in the string value between <> and <>
	// this should include some more error checking
		line = line.trim();
		
		String left = "";
		String value = "";
		String[] splitleft = line.split("<");
		
		if (line.charAt(0)!='<') {
			// line doesn't start with an element name, take all characters up to first '<'
			value = splitleft[0];
		}
		else {
			// take characters after <>
			left = splitleft[1];
			String[] splitright = left.split(">");
			value = splitright[1];
		}
		
		return value;	
	}
	
	private String readElementName (String line) {
	// read in the name of the element after <
		line = line.trim();
		String elName = "";
		
		//System.out.println(line.charAt(0));
		if (line.charAt(0)!='<') { 
			// error checkin: line doesn't start with < 
			System.err.println("Line does not start with an element name");
			elName = line;
		}
		else {
			String[] splitleft = line.split("<");
			String left = splitleft[1];
			String[] splitright = left.split(">");
			String[] splitspace = splitright[0].split(" ");
			elName = splitspace[0];
		}
		return elName;
	}
	
	private boolean check (String line, String expected) {
	// check that the element has the expected name
		String elName = readElementName(line);
		boolean checked = false;
		if (!elName.equals(expected))
		{
			System.err.println("Atom feed contains unexpected element. Element name is:");
			System.err.println(elName);
			System.err.println("Expected:");
			System.err.println(expected);
		}
		else
		{
			checked = true;
		}
		return checked;
	}
	
}